/* modules/console.c 
 * 
 * Copyright (C) 2021, 2022 intirain. 
 *
 * This file is part of evx. 
 * 
 * evx is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * evx is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with evx. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <sys/types.h>

#include <kernel/init.h>
#include <kernel/module.h>
#include <kernel/kmlog.h>
#include <kernel/tty.h>

#include <modules/console.h>

#include <arch/string.h>
#include <arch/io.h>

__uint16_t pos = 0;
__uint16_t x = 0, y = 0;

#define POS2XY(pos) x = pos % 80; y = pos / 80;

static __uint16_t read_cursor(void);
static void update_cursor(void);
static void roll_screen(void);

void raw_putc(__uint16_t, __uint8_t, char);
int cano_putc(char);

static void put_kmlog(void);

struct tty console_tty = {
	.dev_name = "console", 
	.priv_data = 0, 
	.flags = FLAGS_COLORS, 
	.putc = cano_putc, 
	.getc = NULL, 
};

int __init init_console(void) {
	pos = read_cursor();
	POS2XY(pos);

	cano_putc('\n');
	put_kmlog();

	return tty_register(&console_tty);
}

static __uint16_t read_cursor(void) {
	__uint16_t rv = 0;

	outb(CMD_POS_LO, VGA_INDEX);
	rv |= (__uint16_t) inb(VGA_DATA);

	outb(CMD_POS_HI, VGA_INDEX);
	rv |= ((__uint16_t) inb(VGA_DATA)) << 8;

	return rv;
}

int cano_putc(char c) {
	if (c == '\n') {
		y++;
		x = 0;

		goto out;
	}

	if (c == '\r') {
		x = 0;

		goto out;
	}

	if (c == '\t') {
		__uint32_t t = (x + TABSIZE - 1) & ~(TABSIZE - 1);

		if (t == x) 
			x += TABSIZE;
		else 
			x = t;

		if (x == COLMS) {
			y++;
			x = TABSIZE;
		}

		goto out;
	}

	raw_putc(pos++, 7, c);
	x++;

	if (x == COLMS) {
		y++;
		x = 0;
	}

out:	update_cursor();
	return 0;
}

void raw_putc(__uint16_t pos, __uint8_t color, char c) {
	FB_ADDR[pos] = (color << 8) + c;

	return;
}


void update_cursor(void) {
	if (y > LINES) 
		roll_screen();

	pos = y * COLMS + x;

	outb(CMD_POS_LO, VGA_INDEX);
	outb(pos & 0xff, VGA_DATA);

	outb(CMD_POS_HI, VGA_INDEX);
	outb((pos >> 8) & 0xff, VGA_DATA);

	return;
}

void roll_screen(void) {
	/* well, in old versions of xnix this was implemented 
	 * in assembly, that's really fast. 
	 *
	 * but ... now we can only use C. */

	__uint16_t *s;

	memcpy(FB_ADDR, FB_ADDR + 80, 7680);

	/* fill the last line with '0720', white space */

	for (s = FB_ADDR + 1920; s < (__uint16_t *) 0xb9000; s++) 
		*s = 0x0720;

	y = 24;

	return;
}


static void put_kmlog(void) {
	char *s;

	for (s = kmlog_buf; *s; s++) {
		if (s > kmlog_buf + KMLOG_SIZE) 
			s = kmlog_buf;

		cano_putc(*s);
	}

	return;
}


module_init(init_console);


